package com.koron.css2.baseConfig.impl;

import com.koron.css2.baseConfig.bean.BaseCodeRuleBean;
import com.koron.css2.baseConfig.bean.PubCodeRuleBean;
import com.koron.css2.baseConfig.mapper.BaseCodeRuleMapper;
import com.koron.css2.baseConfig.mapper.PubCodeRuleMapper;
import com.koron.css2.common.utils.DateUtils;
import com.koron.css2.serviceManage.utils.RedisUtils;
import com.koron.util.Tools;
import org.apache.commons.lang3.StringUtils;
import org.koron.ebs.mybatis.ADOConnection;
import org.koron.ebs.mybatis.SessionFactory;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 编号生成规则，周新宇修改
 * @author 周利斌
 *
 */
public class GenerateNumberImpl implements GenerateNumber{
	
	private static Logger log = LoggerFactory.getLogger(GenerateNumberImpl.class);
	
	/**
	 * 根据编码规则进行解释并产生编号
	 * {date yy}
		{date MM}
		{date dd}
		{date HH:mm:ss}
		{date HHmmss}
		{date yyyy}
		{date yyyyMMdd}
		{date yyyy-MM-dd}
		{date yyyy/MM/dd}
		{companySample}
		{company}
		{2serial}
		{3serial}
		{4serial}
		{5serial}
		{6serial}
		{7serial}
		{8serial}
		{9serial}
		{10serial}
	 */
	@Override
	public String generate(SessionFactory factory, final String ruleCode) {
		return generate(factory, ruleCode, null);
	}

	@Override
	public String generate(SessionFactory factory, String ruleCode, String waterCode) {
		String wholeNumber1 = "";
		//生成编号锁键
		String repeatCheckKey = waterCode + "_" + ruleCode;
		RLock orderLock = RedisUtils.redisson().getLock(repeatCheckKey);//获取生成编号锁
		orderLock.lock();//加锁
		try {
			wholeNumber1 = ADOConnection.runTask(factory.getDbEnv(), sessionFactory -> {
				String wholeNumber="";
				//设置锁过期时间
				RedisUtils.expire(repeatCheckKey, 60);
				PubCodeRuleBean pubCodeRule = null;
				BaseCodeRuleMapper baseMapper = sessionFactory.getMapper(BaseCodeRuleMapper.class);
				PubCodeRuleMapper pubMaper = sessionFactory.getMapper(PubCodeRuleMapper.class);
				if (pubCodeRule == null) {
					pubCodeRule = pubMaper.selectCodeRule(ruleCode);
				}
				Objects.requireNonNull(pubCodeRule, "未定义 " + ruleCode + "编号生成规则！！！");
				//水司库读取编号信息
				BaseCodeRuleBean baseCodeRule = baseMapper.selectCodeRule(ruleCode);
				if (baseCodeRule == null) {
					//如果水司库还未生成过编号记录，则新创建一条
					baseCodeRule = new BaseCodeRuleBean();
					baseCodeRule.setId(Tools.getObjectId());
					baseCodeRule.setLastSerial(0);
					baseCodeRule.setRuleCode(ruleCode);
					baseMapper.addCodeRule(baseCodeRule);
				}
				boolean f = false;
				int number = baseCodeRule.getLastSerial();
				if (baseCodeRule.getUpdateTime() == null) {
					f = true;
					number = pubCodeRule.getStartSerialNumber();
				} else {
					if (pubCodeRule.getResetCycle() == 1 && DateUtils.betweenYears(new Date(), baseCodeRule.getUpdateTime()) > 0) {
						f = true;
						number = pubCodeRule.getStartSerialNumber();
					}
					if (pubCodeRule.getResetCycle() == 2 && DateUtils.betweenMonths(new Date(), baseCodeRule.getUpdateTime()) > 0) {
						f = true;
						number = pubCodeRule.getStartSerialNumber();
					}
					if (pubCodeRule.getResetCycle() == 3 && DateUtils.betweenDays(new Date(), baseCodeRule.getUpdateTime()) > 0) {
						f = true;
						number = pubCodeRule.getStartSerialNumber();
					}
				}
				final int inumber = f ? number : (number + pubCodeRule.getSerialNumberIncrement());
				baseCodeRule.setLastSerial(inumber);
				baseCodeRule.setUpdateTime(new Date());
				//解释表达式
				String pattern = "\\{.*?\\}";
				Pattern r = Pattern.compile(pattern);
				wholeNumber = pubCodeRule.getRuleExp();
				// 现在创建 matcher 对象
				Matcher matcher = r.matcher(pubCodeRule.getRuleExp());
				while (matcher.find()) {
					String exp = matcher.group();
					if (exp.startsWith("{date ")) {
						String patt = exp.substring(6, exp.length() - 1);
						SimpleDateFormat sdf = new SimpleDateFormat(patt);
						String re = sdf.format(new Date());
						wholeNumber = wholeNumber.replaceAll("\\{date " + patt + "\\}", re);
					} else if (exp.indexOf("serial") > -1) {
						String patt = exp.substring(1, exp.indexOf("serial"));
						Integer ipatt = Integer.parseInt(patt);
						String re = "000000000000000" + inumber;
						re = re.substring(re.length() - ipatt);
						wholeNumber = wholeNumber.replaceAll("\\{" + patt + "serial\\}", re);
					} else if (exp.equals("{companySample}")) {
						if (StringUtils.isBlank(waterCode)) {
							throw new RuntimeException(pubCodeRule.getRuleName() + "编码规则需要传入水司编号");
						}
						//查询水司简化编号
						String sampleCompany = pubMaper.selectCompanySample(waterCode);
						wholeNumber = wholeNumber.replaceAll("\\{companySample\\}", sampleCompany);
					} else if (exp.equals("{company}")) {
						if (StringUtils.isBlank(waterCode)) {
							throw new RuntimeException(pubCodeRule.getRuleName() + "编码规则需要传入水司编号");
						}
						wholeNumber = wholeNumber.replaceAll("\\{company\\}", waterCode);
					}
				}
				baseMapper.updateLastSerial(ruleCode, inumber);
				log.info("{}获取编号成功：{} ", pubCodeRule.getRuleName(), wholeNumber);
				return wholeNumber;
			},String.class);
		}catch (Exception e){
			log.error(repeatCheckKey+":生成编号失败"+e);
		}finally {
			//防止报错导致锁得不到释放
			if (orderLock != null && orderLock.isLocked() && orderLock.isHeldByCurrentThread()) {
				orderLock.unlock();
			}
		}
		return wholeNumber1;
	}

	private static String getString(String waterCode, PubCodeRuleBean pubCodeRule, int inumber, PubCodeRuleMapper pubMaper) {
		//解释表达式
		String pattern = "\\{.*?\\}";
		Pattern r = Pattern.compile(pattern);
		String wholeNumber = pubCodeRule.getRuleExp();
		// 现在创建 matcher 对象
		Matcher matcher = r.matcher(pubCodeRule.getRuleExp());
		while (matcher.find()) {
			String exp= matcher.group();
			if(exp.startsWith("{date ")){
				String patt = exp.substring(6,exp.length()-1);
				SimpleDateFormat sdf = new SimpleDateFormat(patt);
				String re = sdf.format(new Date());
				wholeNumber = wholeNumber.replaceAll("\\{date "+patt+"\\}", re);
			}else if(exp.indexOf("serial")>-1){
				String patt = exp.substring(1,exp.indexOf("serial"));
				Integer ipatt = Integer.parseInt(patt);
				String re = "000000000000000"+ inumber;
				re = re.substring(re.length()-ipatt);
				wholeNumber = wholeNumber.replaceAll("\\{"+patt+"serial\\}", re);
			}else if(exp.equals("{companySample}")){
				if(StringUtils.isBlank(waterCode)) {
					throw new RuntimeException(pubCodeRule.getRuleName()+"编码规则需要传入水司编号");
				}
				//查询水司简化编号
				String sampleCompany = pubMaper.selectCompanySample(waterCode);
				wholeNumber = wholeNumber.replaceAll("\\{companySample\\}", sampleCompany);
			}else if(exp.equals("{company}")){
				if(StringUtils.isBlank(waterCode)) {
					throw new RuntimeException(pubCodeRule.getRuleName()+"编码规则需要传入水司编号");
				}
				wholeNumber = wholeNumber.replaceAll("\\{company\\}", waterCode);
			}
		}
		return wholeNumber;
	}

	@Override
	public String getUserNo(SessionFactory factory, String ruleCode, String waterCode) {
		PubCodeRuleBean pubCodeRule = null;

		BaseCodeRuleMapper baseMapper = factory.getMapper(BaseCodeRuleMapper.class);
		PubCodeRuleMapper pubMaper = factory.getMapper(PubCodeRuleMapper.class);
		if(pubCodeRule == null) {
			pubCodeRule = pubMaper.selectCodeRule(ruleCode);
		}
		Objects.requireNonNull(pubCodeRule, "未定义 " + ruleCode + "编号生成规则！！！");
		//水司库读取编号信息
		BaseCodeRuleBean baseCodeRule = baseMapper.selectCodeRule(ruleCode);
		if(baseCodeRule == null) {
			//如果水司库还未生成过编号记录，则新创建一条
			baseCodeRule = new BaseCodeRuleBean();
			baseCodeRule.setId(Tools.getObjectId());
			baseCodeRule.setLastSerial(0);
			baseCodeRule.setRuleCode(ruleCode);
			baseMapper.addCodeRule(baseCodeRule);
		}
		boolean f = false;
		int number = baseCodeRule.getLastSerial();
		if(baseCodeRule.getUpdateTime() == null) {
			f = true;
			number = pubCodeRule.getStartSerialNumber();
		}else{
			if(pubCodeRule.getResetCycle() == 1 && DateUtils.betweenYears(new Date(), baseCodeRule.getUpdateTime()) > 0) {
				f = true;
				number = pubCodeRule.getStartSerialNumber();
			}
			if(pubCodeRule.getResetCycle() == 2 && DateUtils.betweenMonths(new Date(), baseCodeRule.getUpdateTime()) > 0) {
				f = true;
				number = pubCodeRule.getStartSerialNumber();
			}
			if(pubCodeRule.getResetCycle() == 3 && DateUtils.betweenDays(new Date(), baseCodeRule.getUpdateTime()) > 0) {
				f = true;
				number = pubCodeRule.getStartSerialNumber();
			}
		}
		final int inumber = f ? number : (number + pubCodeRule.getSerialNumberIncrement());
		return getString(waterCode, pubCodeRule, inumber, pubMaper);
	}
}
